@file:Suppress("unused")

package com.hua.customviews.widget

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Rect
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.View.MeasureSpec.EXACTLY
import android.view.View.MeasureSpec.getMode
import android.view.View.MeasureSpec.getSize
import android.view.ViewGroup
import android.widget.Checkable
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import com.hua.customviews.R

/**
 * 流式布局ViewGroup
 * @author Hua
 * @since IMS V1.4.7
 * date: 2023年3月23日
 */
class TagsFlowLayout(
    private val mContext: Context,
    private val attrs: AttributeSet?,
    defStyleAttr: Int
) : ViewGroup(mContext, attrs, defStyleAttr) {
    init {
        initView()
    }

    constructor(mContext: Context, attrs: AttributeSet?) : this(mContext, attrs, 0) {
        initView()
    }

    constructor(mContext: Context) : this(mContext, null, 0) {
        initView()
    }

    //水平View间距
    var horizontalSpacing: Float = 0f

    //垂直View间距
    var verticalSpacing: Float = 0f

    //item布局id
    private var itemLayoutId: Int = View.NO_ID

    //编辑模式下显示的item数量
    private var itemCount: Int = 0
    private var column: Int = 0
    private var row: Int = 0

    var adapter: BaseFlowAdapter? = null
        set(value) {
            field = value
            field?.apply {
                if (itemLayoutId != View.NO_ID) setItemLayoutId(itemLayoutId)
                bindFlowLayout(mContext, this@TagsFlowLayout)
            }
        }

    /**
     * 存放计算宽高数据
     */
    private data class SizeData(var width: Int, var height: Int)

    private fun initView() {
        val typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.TagsFlowLayout)
        typedArray.apply {
            horizontalSpacing = getDimension(R.styleable.TagsFlowLayout_horizontalSpacing, 0f)
            verticalSpacing = getDimension(R.styleable.TagsFlowLayout_verticalSpacing, 0f)
            itemLayoutId = getResourceId(R.styleable.TagsFlowLayout_listItemLayout, View.NO_ID)
            itemCount = getInteger(R.styleable.TagsFlowLayout_itemCount, 0)
            column = getInteger(R.styleable.TagsFlowLayout_maxColumns, 0)
            row = getInteger(R.styleable.TagsFlowLayout_maxRows, 0)
        }
        typedArray.recycle()//编辑模式预览
        if (isInEditMode && itemCount > 0 && itemLayoutId != View.NO_ID) {
            removeAllViews()
            for (i in 0 until itemCount) {
                addView(
                    LayoutInflater.from(mContext).inflate(itemLayoutId, this@TagsFlowLayout, false),
                    i
                )
            }
        }

    }


    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        for (i in 0 until childCount) {
            getChildAt(i).apply {
                //使用在measure过程存放在tag中的layout数据对子view进行layout
                (tag as Rect).apply {
                    layout(left, top, right, bottom)
                }
            }
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        measureChildren(widthMeasureSpec, heightMeasureSpec)
        //测算宽高数据，同时对子view进行layout
        val size = computeSize(widthMeasureSpec, heightMeasureSpec)
        //设置layout的宽高
        setMeasuredDimension(
            if (getMode(widthMeasureSpec) == EXACTLY) getSize(widthMeasureSpec) else size.width,
            if (getMode(heightMeasureSpec) == EXACTLY) getSize(heightMeasureSpec) else size.height
        )
    }

    /**
     * 计算宽高及子View Layout参数
     * @param widthMeasureSpec this的测量空间
     * @param heightMeasureSpec this的测量空间
     * @return SizeData 返回子View总的宽高
     */
    private fun computeSize(widthMeasureSpec: Int, heightMeasureSpec: Int): SizeData {
        val maxWidth = getSize(widthMeasureSpec)
        val sizeData = SizeData(paddingStart + paddingEnd, paddingTop + paddingBottom)

        var rowMaxHeight = 0 //当前行最大高度
        var colMaxWidth = 0
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            if (!child.isShown && !isInEditMode) {
                if (i == childCount - 1) {
                    sizeData.height += rowMaxHeight
                    sizeData.width = colMaxWidth
                }
                //该控件被隐藏了
                continue
            }
            //获取元素的margin
            val marginParams = child.layoutParams as MarginLayoutParams
            val childWidth =
                marginParams.marginStart + marginParams.marginEnd +
                        (if (column == 0) child.measuredWidth
                        else ((maxWidth - paddingStart - paddingEnd - (column - 1) * horizontalSpacing) / column).toInt())
            child.measure(
                getChildMeasureSpec(widthMeasureSpec, paddingStart + paddingEnd, childWidth),
                getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, child.layoutParams.height)
            )
            val childHeight =
                marginParams.topMargin + marginParams.bottomMargin + child.measuredHeight
            //刷新最大行高
            rowMaxHeight = rowMaxHeight.coerceAtLeast(childHeight)
            sizeData.apply {
                //换行时累计高度
                if (width + childWidth + (if (width != paddingStart + paddingEnd) horizontalSpacing.toInt() else 0) > maxWidth) {
                    //重置行宽度
                    width = paddingStart + paddingEnd
                    //累计高度
                    height += rowMaxHeight + verticalSpacing.toInt()
                    rowMaxHeight = childHeight
                }
                //累计宽度
                width += childWidth + if (width != paddingStart + paddingEnd) horizontalSpacing.toInt() else 0
                colMaxWidth = colMaxWidth.coerceAtLeast(width)
                //将child的layout位置保存到tag中
                child.tag = Rect(
                    width - childWidth + marginParams.marginStart,
                    height + marginParams.topMargin,
                    width - marginParams.marginEnd,
                    height + childHeight - marginParams.bottomMargin
                )
                if (i == childCount - 1) {
                    height += rowMaxHeight
                    width = colMaxWidth
                }
            }
        }
        return sizeData
    }

    /**
     * 防止LayoutParams转换失败
     */
    override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
        return MarginLayoutParams(context, attrs)
    }

    override fun generateDefaultLayoutParams(): LayoutParams {
        return MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
    }

    override fun generateLayoutParams(p: LayoutParams?): LayoutParams {
        return MarginLayoutParams(p)
    }

}

/**
 * Adapter接口
 */
interface BaseFlowAdapter {
    fun createItemView(): View
    fun bindFlowLayout(context: Context, layout: TagsFlowLayout)
    fun setItemLayoutId(@LayoutRes layoutId: Int)
}

/**
 * TagsFlowLayout适配器
 * @param layoutId item布局id
 * @param mData 布局数据
 */
abstract class TagsFlowAdapter<T>(@LayoutRes var layoutId: Int, var mData: MutableList<T>?) :
    BaseFlowAdapter {
    init {
        initAdapter()
    }

    constructor(@LayoutRes layoutId: Int) : this(layoutId = layoutId, mData = mutableListOf()) {
        initAdapter()
    }

    constructor(data: MutableList<T>?) : this(
        layoutId = View.NO_ID,
        mData = data ?: mutableListOf()
    ) {
        initAdapter()
    }

    /**
     * 初始化adapter
     */
    private fun initAdapter() {
        if (mData == null) mData = mutableListOf()
    }

    //上下文
    var mContext: Context? = null

    //绑定的 TagsFlowLayout 容器
    var mLayout: TagsFlowLayout? = null

    //itemView列表
    private val childViewList = mutableListOf<View>()

    //需要设置点击事件的view列表

    //item点击监听
    private var onItemClickListener: ((view: View, position: Int) -> Unit)? = null
    private var onItemLongClickListener: ((view: View, position: Int) -> Unit)? = null
    private var onItemChildClickListener: ((view: View, position: Int) -> Unit)? = null
    private var onItemChildLongClickListener: ((view: View, position: Int) -> Unit)? = null

    /**
     * 设置item点击监听器
     */
    fun setOnItemClickListener(onItemClickListener: (view: View, position: Int) -> Unit) {
        this.onItemClickListener = onItemClickListener
        notifyDataSetChanged()
    }

    /**
     * 设置item点击监听器
     */
    fun setOnItemLongClickListener(onItemLongClickListener: (view: View, position: Int) -> Unit) {
        this.onItemLongClickListener = onItemLongClickListener
        notifyDataSetChanged()
    }

    /**
     * 设置item点击监听器
     */
    fun setOnItemChildClickListener(onItemChildClickListener: (view: View, position: Int) -> Unit) {
        this.onItemChildClickListener = onItemChildClickListener
        notifyDataSetChanged()
    }

    /**
     * 设置item点击监听器
     */
    fun setOnItemChildLongClickListener(onItemChildLongClickListener: (view: View, position: Int) -> Unit) {
        this.onItemChildLongClickListener = onItemChildLongClickListener
        notifyDataSetChanged()
    }

    /**
     * 在对 TagsFlowLayout 设置adapter属性时，自动绑定关系，获取context
     */
    override fun bindFlowLayout(context: Context, layout: TagsFlowLayout) {
        this.mContext = context
        this.mLayout = layout
        notifyDataSetChanged()
    }

    /**
     * 供外部adapter设置itemView
     */
    abstract fun convert(viewHolder: TagsFlowViewHolder, item: T, position: Int)

    /**
     * inflate一个新的item
     */
    override fun createItemView(): View {
        if (layoutId == View.NO_ID) throw IllegalStateException("Please set resource id in the adapter or in the xml file first!!")
        return LayoutInflater.from(mContext).inflate(layoutId, mLayout, false)
    }

    /**
     * 用于从xml中读取所设置的item
     */
    override fun setItemLayoutId(layoutId: Int) {
        if (layoutId != View.NO_ID) this.layoutId = layoutId
    }

    /**
     * 设置新的item数据
     */
    fun setNewData(data: MutableList<T>?) {
        mData = data ?: mutableListOf()
        notifyDataSetChanged()
    }

    /**
     * 替换item数据
     */
    fun replaceData(data: MutableCollection<T>) {
        if (mData != data && mData != null) {
            mData?.clear()
            mData?.addAll(data)
        }
        notifyDataSetChanged()
    }

    /**
     * 数据变更后刷新view
     */
    fun notifyDataSetChanged() {
        childViewList.clear()
        if (mLayout != null) mLayout!!.removeAllViews()
        mData?.forEachIndexed { index, item ->
            val itemView = createItemView()
            val holder = TagsFlowViewHolder(itemView)
            //设置点击listener
            setItemViewClick(itemView, index)
            //外部view操作
            convert(holder, item, index)
            //保存view集和点击事件view集
            childViewList.add(index, itemView)
            //设置点击事件
            setItemViewChildClick(
                holder.getOnClickList(),
                holder.getOnLongClickList(),
                position = index
            )
        }
        //添加view到layout中
        if (mLayout != null) childViewList.forEachIndexed { index, view ->
            mLayout!!.addView(view, index)
        }
    }

    private fun setItemViewClick(itemView: View, position: Int) {
        //设置item点击事件
        if (onItemClickListener != null) {
            if (!itemView.isClickable) itemView.isClickable = true
            itemView.setOnClickListener {
                onItemClickListener!!.invoke(it, position)
            }
        }
        if (onItemLongClickListener != null) {
            if (!itemView.isLongClickable) itemView.isLongClickable = true
            itemView.setOnLongClickListener {
                onItemLongClickListener!!.invoke(it, position)
                false
            }
        }
    }

    /**
     * 设置需要设置点击事件的View onClickListener
     */
    private fun setItemViewChildClick(
        clickViewList: MutableList<View>,
        longClickViewList: MutableList<View>,
        position: Int
    ) {
        if (clickViewList.isNotEmpty() && onItemChildClickListener != null) clickViewList.forEach { view ->
            if (!view.isClickable) view.isClickable = true
            view.setOnClickListener {
                onItemChildClickListener!!.invoke(it, position)
            }
        }
        if (longClickViewList.isNotEmpty() && onItemChildLongClickListener != null) longClickViewList.forEach { view ->
            if (!view.isLongClickable) view.isLongClickable = true
            view.setOnLongClickListener {
                onItemChildLongClickListener!!.invoke(it, position)
                false
            }
        }
    }

}

/**
 * 视图控制器，提供各类属性setter方法
 */
class TagsFlowViewHolder(private val view: View) {
    private val clickViewList = mutableListOf<View>()
    private val longClickViewList = mutableListOf<View>()

    /**
     * 获取itemView中的子View
     */
    fun <T : View> getViewById(@IdRes id: Int): T = view.findViewById(id)

    /**
     * 设置文字
     * @param id View资源id
     * @param value
     */
    fun setText(@IdRes id: Int, value: CharSequence): TagsFlowViewHolder {
        getViewById<TextView>(id).text = value
        return this
    }

    /**
     * 设置文字
     * @param id View资源id
     * @param value
     */
    fun setText(@IdRes id: Int, @StringRes value: Int): TagsFlowViewHolder {
        getViewById<TextView>(id).setText(value)
        return this
    }

    /**
     * 设置背景颜色
     * @param id View资源id
     * @param value 颜色资源
     */
    fun setBackgroundColor(@IdRes id: Int, @ColorInt value: Int): TagsFlowViewHolder {
        getViewById<View>(id).setBackgroundColor(value)
        return this
    }

    /**
     * 设置背景图片
     * @param id View资源id
     * @param value 图片资源
     */
    fun setBackgroundRes(@IdRes id: Int, @DrawableRes value: Int): TagsFlowViewHolder {
        getViewById<View>(id).setBackgroundResource(value)
        return this
    }

    /**
     * 设置文字颜色
     * @param id View资源id
     * @param textColor 颜色
     */
    fun setTextColor(@IdRes id: Int, @ColorInt textColor: Int): TagsFlowViewHolder {
        getViewById<TextView>(id).setTextColor(textColor)
        return this
    }

    /**
     * 设置文字大小
     * @param id View资源id
     * @param size 字体大小（须自行转换px）
     */
    fun setTextSize(@IdRes id: Int, size: Float): TagsFlowViewHolder {
        getViewById<TextView>(id).textSize = size
        return this
    }

    /**
     * 设置文字样式
     * @param id View资源id
     * @param typeface 显示样式
     */
    fun setTextTypeface(@IdRes id: Int, typeface: Typeface): TagsFlowViewHolder {
        getViewById<TextView>(id).typeface = typeface
        return this
    }

    /**
     * 设置Drawable资源
     * @param id View资源id
     * @param drawable Drawable
     */
    fun setImageDrawable(@IdRes id: Int, drawable: Drawable): TagsFlowViewHolder {
        getViewById<ImageView>(id).setImageDrawable(drawable)
        return this
    }

    /**
     * 设置位图
     * @param id View资源id
     * @param bitmap 位图
     */
    fun setImageBitmap(@IdRes id: Int, bitmap: Bitmap): TagsFlowViewHolder {
        getViewById<ImageView>(id).setImageBitmap(bitmap)
        return this
    }

    /**
     * 设置透明度
     * @param id View资源id
     * @param value 透明度 0f - 1f
     */
    fun setAlpha(@IdRes id: Int, value: Float): TagsFlowViewHolder {
        getViewById<View>(id).alpha = value
        return this
    }

    /**
     * 设置是否显示
     * @param id View资源id
     * @param visible true显示 false不显示
     */
    fun setGone(@IdRes id: Int, visible: Boolean): TagsFlowViewHolder {
        getViewById<View>(id).visibility = if (visible) View.VISIBLE else View.GONE
        return this
    }

    /**
     * 设置是否可见
     * @param id View资源id
     * @param visible true可见 false不可见
     */
    fun setVisible(@IdRes id: Int, visible: Boolean): TagsFlowViewHolder {
        getViewById<View>(id).visibility = if (visible) View.VISIBLE else View.INVISIBLE
        return this
    }

    /**
     * 设置View标签
     * @param id View资源id
     * @param tag 标签内容
     */
    fun setTag(@IdRes id: Int, tag: Any?): TagsFlowViewHolder {
        getViewById<View>(id).tag = tag
        return this
    }

    /**
     * 设置View标签
     * @param id View资源id
     * @param key 标签key
     * @param tag 标签内容
     */
    fun setTag(@IdRes id: Int, key: Int, tag: Any?): TagsFlowViewHolder {
        getViewById<View>(id).setTag(key, tag)
        return this
    }

    /**
     * 设置选中
     * @param id View资源id
     * @param checked 选中状态
     */
    fun setChecked(@IdRes id: Int, checked: Boolean): TagsFlowViewHolder {
        val view = getViewById<View>(id)
        if (view is Checkable) (view as Checkable).isChecked = checked
        return this
    }

    /**
     * 添加可点击的view
     * @param id View资源id
     */
    fun addOnClick(@IdRes id: Int): TagsFlowViewHolder {
        clickViewList.add(getViewById(id))
        return this
    }

    /**
     * 添加可长按的view
     * @param id View资源id
     */
    fun addOnLongClick(@IdRes id: Int): TagsFlowViewHolder {
        longClickViewList.add(getViewById(id))
        return this
    }

    /**
     * 获取此item的即将添加点击事件的view列表
     */
    fun getOnClickList() = clickViewList

    /**
     * 获取此item的即将添加长按点击事件的view列表
     */
    fun getOnLongClickList() = longClickViewList
}

